import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/helper/db/ute_db_helper.dart';
import 'package:share_fit/app/helper/ext/tr_ext.dart';
import 'package:share_fit/app/models/home/date_type_enum.dart';
import 'package:share_fit/app/models/home/stress_model.dart';
import 'package:share_fit/app/tools/locale_tool.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/generated/locales.g.dart';

import 'state.dart';

class StressLogic extends BaseController {
  final StressState state = StressState();

  goBack() {
    Get.back();
  }

  changeDateType({type, int? selectedIndex}) {
    state.selectedDateType.value = type;
    state.dateTypeBarIndex = selectedIndex ?? 0;
    update();

    if (type == DateType.year) {
      for (var month = 1; month <= 12; month++) {
        // state.dates.add('$month');
      }
    } else if (type == DateType.week) {
      List<DateTime> dts = type.getWeekDays;
      queryInRange(dts.first, dts.last);
    } else if (type == DateType.month) {
      List<DateTime> dts = type.getMonthDays;
      queryInRange(dts.first, dts.last);
    } else {
      queryTodayData(state.selectedDate);
    }
    update(['calendar_btn_icon', 'date_type_bar']);
  }

  /// 右上角日历选择
  datePicker(dt) {
    if (dt != null) {
      state.selectedDate = dt;
      state.selectedDateType.value = DateType.day;

      update(['calendar_btn_icon']);

      queryTodayData(dt);

      queryDataDetail(dt, dt);
    }
  }

  String getRatioTitle() {
    if (state.selectedDateType.value == DateType.day) {
      return LocaleKeys.stress_today_ratio_text.ts;
    } else if (state.selectedDateType.value == DateType.week) {
      return LocaleKeys.stress_week_ratio_text.ts;
    } else if (state.selectedDateType.value == DateType.month) {
      return LocaleKeys.stress_month_ratio_text.ts;
    } else {
      return LocaleKeys.stress_month_ratio_text.ts;
    }
  }

  Future<void> queryDataDetail(DateTime start, DateTime end) async {
    String dateStart = DateTool.getYMDStr(start);
    String dateEnd = DateTool.getYMDStr(end);
    UteDbHelper.getDB().then((db) => //
        db.stressDao.findDataDetail(getAddress(), dateStart, dateEnd).then((result) {
          if (result.isNotEmpty) {
            int max = result[0].pressureValue!;
            int min = result[0].pressureValue!;
            int avg = 0;

            for (var i = 0; i < result.length; i++) {
              int pv = result[i].pressureValue!;

              avg += pv;

              // Checking for largest value in the list
              if (pv > max) {
                max = pv;
              }

              // Checking for smallest value in the list
              if (pv < min) {
                min = pv;
              }
            }

            state.dataDetail['avg_pressure_value'] = '${avg ~/ result.length}';
            state.dataDetail['min_pressure_value'] = '$min';
            state.dataDetail['max_pressure_value'] = '$max';
          } else {
            state.dataDetail['avg_pressure_value'] = '--';
            state.dataDetail['min_pressure_value'] = '--';
            state.dataDetail['max_pressure_value'] = '--';
          }

          update(['dataDetail']);
        }));
  }

  Future<void> queryTodayData(DateTime start) async {
    _updateDateRange(start: null, end: start);

    UteDbHelper.getDB().then((db) {
      //
      // 当天的压力只显示最近一次的压力值
      db.stressDao.findLatestOne(getAddress(), DateTool.getYMDStr(start)).then((value) => _onUpdateCurrentPressure([value ?? StressModel()]));

      db.stressDao.groupByMinute(getAddress(), DateTool.getYMDStr(start)).then((result) {
        result.forEach((e) => e.setLevel());
        _onUpdateChart(result, null, null);
        _onUpdateRatio(result);
      });
    });

    // 读取平均值、最大值、最小值
    queryDataDetail(start, start);
  }

  Future<void> queryInRange(DateTime start, DateTime end) async {
    String dateStart = DateTool.getYMDStr(start);
    String dateEnd = DateTool.getYMDStr(end);

    _updateDateRange(start: start, end: end);

    UteDbHelper.getDB().then((db) => //
        db.stressDao.findInRange(getAddress(), dateStart, dateEnd).then((result) {
          result.forEach((e) => e.setLevel());
          // 周月的压力需要计算平均的压力值
          _onUpdateCurrentPressure(result);
          _onUpdateChart(result, start, end);
          _onUpdateRatio(result);
        }));

    // 读取平均值、最大值、最小值
    queryDataDetail(start, end);
  }

  void _updateDateRange({DateTime? start, required DateTime end}) {
    if (start == null) {
      state.dateRangeText = DateTool.ymdFormat(end);
    } else {
      state.dateRangeText = '${DateTool.ymdFormat(start)} ~ ${DateTool.ymdFormat(end)}';
    }
    update(['sleep_date_range']);
  }

  Future<void> _onUpdateChart(List<StressModel> sms, DateTime? start, DateTime? end) async {
    List<StressModel> models = [];

    if (state.selectedDateType.value == DateType.day) {
      List.generate(25, (index) {
        StressModel model = sms.singleWhere(
          (e) => (e.calendar!.contains(' ') ? int.parse(e.calendar!.split(' ')[1]) :  int.parse(e.calendar!.split(':')[0])) == index,
          orElse: () => StressModel(pressureValue: 0, calendar: '${index < 10 ? '0$index' : '$index'}:00', level: 0),
        );

        if (model.pressureValue! > 0) {
          List arr = model.calendar!.split(' ');
          String hour = arr.length == 1 ? arr[0] : arr[1];
          model.calendar = '$hour:00';
          model.calendarTime = '$hour:00~${int.parse(hour) + 1}:00';
        }

        models.add(model);
      });
    } else {
      String langCode = LocaleTool.locale.split('_')[0];
      int days = end!.difference(start!).inDays;

      for (int i = 0; i <= days; i++) {
        DateTime temp = start.add(Duration(days: i));
        String calendar = days == 6 ? DateFormat.E(langCode).format(temp) : DateTool.mdFormat(temp);
        String calendarTime = DateTool.getYMDWithDash(temp);
        StressModel matched = sms.singleWhere(
          (e) => e.calendar == calendarTime,
          orElse: () => StressModel(calendarTime: calendarTime, calendar: calendar, pressureValue: 0, level: 0),
        );

        if (matched.pressureValue! > 0) {
          matched.calendar = calendar;
          matched.calendarTime = calendarTime;
        }

        models.add(matched);
      }
    }

    state.modelList = models;

    update(['stress_chart']);
  }

  _onUpdateRatio(List<StressModel> sms) {
    int lowCount = sms.where((element) => element.level == 1).length;
    int normalCount = sms.where((element) => element.level == 2).length;
    int mediumCount = sms.where((element) => element.level == 3).length;
    int heightCount = sms.where((element) => element.level == 4).length;

    int total = lowCount + normalCount + mediumCount + heightCount;

    state.ratioList = [
      PressureRatio(
        _getRatio(total, lowCount),
        '${LocaleKeys.stress_quality_low_text.ts}（1-29）',
        const Color.fromRGBO(19, 208, 255, 1),
      ),
      PressureRatio(
        _getRatio(total, normalCount),
        '${LocaleKeys.stress_quality_normal_text.ts}（30-59）',
        Colors.greenAccent,
      ),
      PressureRatio(
        _getRatio(total, mediumCount),
        '${LocaleKeys.stress_quality_medium_text.ts}（60-79）',
        const Color.fromRGBO(181, 124, 255, 1),
      ),
      PressureRatio(
        _getRatio(total, heightCount),
        '${LocaleKeys.stress_quality_height_text.ts}（80-100）',
        Colors.red.withOpacity(.7),
      ),
    ];

    update(['pressure_ratio']);
  }

  _onUpdateCurrentPressure(List<StressModel> sm) {
    int? sum = sm.isEmpty ? null : sm.map((e) => e.pressureValue).reduce((value, element) => value! + element!);
    state.currentPressureText = '${sum == null ? '--' : sum ~/ sm.length}';
    update(['current_pressure']);
  }

  _getRatio(int total, int sum) {
    double ratio = sum / total;
    return ratio.isNaN ? 0.0 : ratio;
  }

  @override
  String getRemotePath() {
    return '';
  }

  @override
  void initState() {
    queryTodayData(DateTime.now());
    _loadDateListOfRecord();
  }

  @override
  void onRemoteEvent(event, method) {
    // TODO: implement onRemoteEvent
  }

  loadEventForDay(DateTime day) {
    Map<DateTime, List<bool>> events = LinkedHashMap<DateTime, List<bool>>(
      equals: DateTool.isSameDay,
      hashCode: (key) => key.day * 1000000 + key.month * 10000 + key.year,
    )..addAll(state.dateListOfRecord);

    return events[day] ?? [];
  }

  /// 查找指定年份中有压力记录的日期
  Future<void> _loadDateListOfRecord() async {
    DateTime now = DateTime.now();
    DateTime lastYear = DateUtils.addMonthsToMonthDate(now, -12);
    UteDbHelper.getDB().then((db) => //
        db.stressDao.findHasRecordDays(getAddress(), lastYear.year.toString(), now.year.toString()).then((result) {
          state.dateListOfRecord.clear();
          result.forEach((element) {
            state.dateListOfRecord.putIfAbsent(DateTime.parse(element), () => [true]);
          });
        }));
  }
}
